#ifndef SNACK_PLAYMANAGER_IMPL_H
#define SNACK_PLAYMANAGER_IMPL_H

#include "PlayManager.h"

#include "TrackImplementation.h"
#include "EventImplementation.h"
#include "FMODSystem.h"

#include "api/inc/fmod.hpp"
#include "api/inc/fmod_errors.h"

#include <vector>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <map>

namespace SnackBreak
{
namespace Impl
{

typedef std::vector<FMODTrack*> TrackCollection;
typedef std::vector<SyncEvent*> MixCollection;
//typedef std::vector<FMODMixEvent*> MixCollection; // how polymorphic should this list be? or just have lists per mix type?
typedef std::map<const std::string, SyncEvent*> MixMap;
typedef std::vector<AutomationEvent*> AutoMixCollection;

class FMODPlayManager : public PlayManager
{
public:
	FMODPlayManager();
	~FMODPlayManager();

	static FMODPlayManager& Get();

	// client methods
	FMODTrack* CreateTrack(std::string f, CuePair q);
	FMODTrack* CreateTrack(std::string f, CuePair q, double lastBeatNum);
	FMODMixEvent* CreateMix(Track* outgoing, Track* incoming);
	FMODMixEvent* CreateMix(Track* outgoing, Track* incoming, double numBeatsToOverlap);
	FMODMixEvent* CreateEarlyMix(Track* outgoing, Track* incoming, double numBeatsFromStart);
	FMODMixEvent* CreateBlend(Track* outgoing, Track* incoming, double numBeatsToOverlap, double faderBlendBeats, double incomingStartBeat);
	FMODMixEvent* CreateEarlyBlend(Track* outgoing, Track* incoming, double numBeatsFromStart, double faderBlendBeats, double incomingStartBeat);

	// mix/internal impl methods
	void FindDropsOffChannel(FMOD::Channel* ch, MixCollection& mixes);
	void FindMixesOffChannel(FMOD::Channel* ch, char* name, MixCollection& mixes);
	
private:
	// validation of Track*, Mix* pointers
	bool IsValid(Track* suspect) { return std::find(tracks.begin(), tracks.end(), suspect) != tracks.end(); };

	// storage for all the tracks in play
	TrackCollection tracks;

	// likewise for mixes in play
	// (in separate collections because that's the first search param in the callback)
	MixCollection drops; // non-mixes - end-of-track-play-this-track
	MixMap mixes; // mixes - something happens at a sync point (stored in a map because we can only get the name in the callback)
	AutoMixCollection automixes;

private:
	// we only have one callback, so we need to set it up once, independent of any actual events
	static void ArmDSP(FMODPlayManager* playMan);
	static void KillDSP();
	static FMOD::DSP* dspObject;
	static FMOD_RESULT F_CALLBACK AutomationCallback(FMOD_DSP_STATE *  dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
};

}; // namespace Impl
}; // namespace SnackBreak

#endif // SNACK_PLAYMANAGER_IMPL_H
